package com.scarecrow.olga.entity {
	
	import com.scarecrow.olga.entity.IEntity;
	import com.scarecrow.olga.error.EntityRegistryError;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;
	
	/**
	 * The EntityRegistry keeps track of all active entities in the application.
	 * Every time an entity is created, it automatically registers itself into 
	 * the registry and a uinque identifier is created for it. 
	 * 
	 * When an entity is removed, it unregisters itself from the EntityRegistry.
	 * The registry can be used to retrieve an entity from anywhere, by providing
	 * the entity's ID.
	 * 
	 * The amount of registered entities is also tracked by this class for 
	 * profiling or other purposes.
	 * ...
	 * @author Santiago.J.Elizalde
	 */
	public class EntityRegistry {
		
		private static var _entityCount:uint;
		
		private static var entities:Dictionary = new Dictionary();
		private static var entitiesById:Dictionary = new Dictionary(true);
		private static var entitiesByName:Dictionary = new Dictionary();
		
		internal static function getUID():String {
			return Math.floor(getTimer()) + "-" + (entityCount + 1);
		}
		
		internal static function registerEntity(entity:IEntity):void {
			if(entity in entities) {
				throw new EntityRegistryError("Duplicate entity registry.", "The entity  supplied for registry is already registered.", entity);
			}
			entities[entity] = entity.id;
			entitiesById[entity.id] = entity;
			_entityCount++;
		}
		
		internal static function releaseEntity(entity:IEntity):void {
			if(!entity in entities) {
				throw new EntityRegistryError("Nonexistant entity registry.", "The entity supplied for release is not registered.", entity);
			}
			delete entities[entity];
			delete entitiesById[entity.id];
			var list:Vector.<IEntity> = entitiesByName[entity.name];
			if (list && list.indexOf(entity)) {
				list.splice(list.indexOf(entity), 1);
			}
			_entityCount--;
		}
		
		/**
		 * Retrieves an entity registered with the supplied ID.
		 * Will return undefined if a registry with the supplied ID does not exist.
		 * 
		 * @param	entityId the ID of the desired entity.
		 * @return	the entity registered by the supplied ID.'undefined' if the regitry does not exist.
		 */
		public static function getEntity(entityId:String):IEntity {
			return entitiesById[entityId];
		}
		
		/**
		 * Returns a list of entities with the specified name. This is a list because entity names are 
		 * non-unique, and thus can be used to group and categorize entities.
		 * 
		 * note: this function will perform slower the first time it is acalled with each name, because
		 * lists are created on demand to save memory and entity instantiation cost.
		 * 
		 * @param	name
		 * @return	the list of entities with the specified name.
		 */
		public static function getEntitiesByName(name:String):Vector.<IEntity> {
			if (!entitiesByName[name]) {
				var list:Vector.<IEntity> = new Vector.<IEntity>();
				for each (var entity:IEntity in entities) {
					if (entity.name == name) list.push(entity);
				}
			}
			return entitiesByName[name];
		}
		
		public static function get entityCount():uint {
			return _entityCount;
		}
		
		public function EntityRegistry() {
			
		}
		
	}
}